import { AssetManager, ImageAsset, Node, Prefab, SpriteFrame, TextAsset, Texture2D, assetManager, director, find, instantiate, sp } from "cc";
import Singleton from "../bo/Singleton";
import { BundleType } from "../../Enum/FramworkEnums";
/**
 * @author xxkk(小袖空空)
 * @description 加载分包管理 bundle管理
 */
export default class BundleManager extends Singleton {
    private bundleMap: Map<BundleType, AssetManager.Bundle> = new Map<BundleType, AssetManager.Bundle>();

    public static get Instance(): BundleManager {
        return super.GetInstance<BundleManager>();
    }
    clear() {
        this.bundleMap = new Map<BundleType, AssetManager.Bundle>();
    }

    async initRescources() {// resources bundle 加载到内存引用
        await this.initBundle(BundleType.resourcesBundle);
    }
    /**
     * 加载bundle
     * @param bundleType 
     * @returns 
     */
    public async initBundle(bundleType: BundleType) {
        if (this.getBundle(bundleType) != null) {
            console.log("loaded", bundleType);
            return;
        }
        await new Promise<void>((resolve, reject) => {
            assetManager.loadBundle(bundleType, (err, bundle) => {
                if (err)
                    reject(err)
                this.bundleMap.set(bundleType, bundle);
                console.log("loading", bundleType, bundle.base);
                resolve();
            });
        });
        // const keys: any = Object.keys(BundleType).map(key => BundleType[key]).filter(value => typeof value === 'number');
        // console.log(this.bundleMap);
        // console.log(this.bundleMap.get(BundleType.hall));
        // 当复用其他项目的 Asset Bundle 时
        // assetManager.loadBundle('https://othergame.com/remote/01_graphics', (err, bundle) => {
        //     bundle.load('xxx');
        // });
    }
    // 手动指定bundle版本
    // assetManager.loadBundle('01_graphics', {version: 'fbc07'}, function (err, bundle) {
    //     if (err) {
    //         return console.error(err);
    //     }
    //     console.log('load bundle successfully.');
    // });

    public getBundle(bundleType: BundleType = BundleType.hall) {
        return this.bundleMap.get(bundleType);
    }

    /**资源加载基础函数 
     * @param cb 函数回调
    */
    public loadRes(bundle: AssetManager.Bundle, url: string, type: any, cb: Function = () => { }) {
        bundle.load(url, type, (err: any, res: any) => {
            if (err) {
                console.log("loadRes err", err);
                cb(err, res);
                return;
            }
            cb && cb(null, res);
        })
    }
    /**
    * 预加载资源
    * @param sceneName 
    * @param bundleType 
    * @returns 
    */
    public preloadRes(bundle: AssetManager.Bundle, url: string, type: any, onProgress: ((finished: number, total: number, item: any) => void) | null, onComplete: ((err: Error | null, data: any[]) => void) | null) {
        bundle.preload(url, type, onProgress, onComplete)
    }

    /**
    * 加载资源
    * @param url   资源路径
    * @param type  资源类型
    * @method loadRes
    */
    public loadResType(url: string, type: any, bundleType: BundleType = BundleType.resourcesBundle) {
        let bundle = this.bundleMap.get(bundleType);
        return new Promise<any>((resolve, reject) => {
            bundle.load(url, type, (err: any, res: any) => {
                if (err) {
                    console.error(err.message || err);
                    reject && reject(err)
                    return;
                }

                resolve && resolve(res);
            })
        })
    }

    //=========================================================================================================================
    // 异步加载场景
    loadScenceSync(sceneName: string, bundleType?: BundleType) {
        return new Promise<void>((resolve) => {
            this.loadScence(sceneName, () => { resolve() }, bundleType);
        });
    }
    // 加载场景
    loadScence(sceneName: string, cb?: () => void, bundleType: BundleType = BundleType.hall) {
        let bundle = this.bundleMap.get(bundleType);
        // let key = Object.keys(BundleType).find(key => BundleType[key] === bundleType);
        bundle.loadScene(sceneName, function (err, scene) {
            director.runScene(scene);
        });
        cb && cb();
    }
    // 加载特效使用
    public loadEffectRes(modulePath: string, bundleType: BundleType = BundleType.resourcesBundle) {
        let bundle = this.getBundle(bundleType);
        return new Promise((resolve, reject) => {
            this.loadRes(bundle, `prefab/effect/${modulePath}`, Prefab, (err: any, prefab: Prefab) => {
                if (err) {
                    console.error('effect load failed', modulePath);
                    reject && reject();
                    return;
                }
                resolve && resolve(prefab);
            })
        });
    }
    /**加载骨骼动画数据sp.skeleton 由于材质影响 还是改用预制体 弃用
     * */
    // public loadSpineRes(modulePath: string,cb:()=>void, bundleType: BundleType = BundleType.resourcesBundle) {
    //     const bundle = this.getBundle(bundleType)
    //     this.loadRes(bundle, modulePath, sp.Skeleton, cb)
    // }
    /** 异步加载预制体
     * @param modulePath 在prefab/ui/的中路径
     * */
    public loadPrefab(modulePath: string, bundleType: BundleType = BundleType.resourcesBundle) {
        let bundle = this.getBundle(bundleType);
        return new Promise((resolve, reject) => {
            this.loadRes(bundle, `prefab/ui/${modulePath}`, Prefab, (err: any, prefab: Prefab) => {
                if (err) {
                    console.error('prefab load failed', modulePath);
                    reject && reject();
                    return;
                }
                resolve && resolve(prefab);
            })
        });
    }
    /** 异步预加载预制体
   * @param modulePath 在prefab/ui/的中路径
   * */
    public preloadPrefabAsync(modulePath: string, bundleType: BundleType = BundleType.resourcesBundle, onProgress: ((finished: number, total: number, item: any) => void), onComplete: ((err: Error | null, data: any[]) => void)) {
        let bundle = this.getBundle(bundleType);
        return new Promise((resolve, reject) => {
            this.preloadRes(bundle, `prefab/ui/${modulePath}`, Prefab, onProgress, onComplete)
        });
    }
    /** 使用回调加载预制体*/
    public loadPrefab_(modulePath: string, bundleType: BundleType = BundleType.resourcesBundle, cb: Function = (err: any, prefab: Prefab) => { }) {
        let bundle = this.getBundle(bundleType);
        this.loadRes(bundle, modulePath, Prefab, cb)
        //(err: any, prefab: Prefab) => {
        //             if (err) {
        //                 console.error('effect load failed', modulePath);
        //                 return null;
        //             }
        //             return prefab;
        //         }
    }

    public getUIPrefabRes(bundle: AssetManager.Bundle, prefabPath: string, cb?: Function) {
        this.loadRes(bundle, "prefab/ui/" + prefabPath, Prefab, cb);
    }

    public createUI(path: string, cb?: Function, isTip?: boolean, bundleType: BundleType = BundleType.resourcesBundle) {
        let bundle = this.getBundle(bundleType);
        this.getUIPrefabRes(bundle, path, function (err: {}, prefab: Prefab) {
            if (err) return;
            let node: Node = instantiate(prefab);
            node.setPosition(0, 0, 0);
            var parent: Node = null;
            if (isTip) {
                parent = find("Canvas/ui/tip") as Node;
            } else {
                parent = find("Canvas/ui/dislog") as Node;
            }
            parent.addChild(node);

            cb && cb(null, node);
        });
    }

    // 加载 Texture
    // loadTexture(bundleType: BundleType, cb?: () => void) {
    //     let bundle = this.bundleMap.get(bundleType);
    //     bundle.load(`image/texture`, Texture2D, function (err, texture) {
    //         console.log(texture)
    //     });
    // }
    // 加载 SpriteFrame
    public loadSprite(path: string, bundleType: BundleType = BundleType.resourcesBundle) {
        let bundle = this.bundleMap.get(bundleType);
        return new Promise((resolve, reject) => {
            bundle.load(`image/spriteFrame/${path}`, function (err, img: ImageAsset) {
                if (err) {
                    console.error('spriteFrame load failed!', path, err);
                    reject && reject();
                    return;
                }
                let texture = new Texture2D();
                texture.image = img;
                let sf = new SpriteFrame();
                sf.texture = texture;
                resolve && resolve(sf);
            });
        });
    }
    public loadSprites(path: string, bundleType: BundleType = BundleType.resourcesBundle) {
        let bundle = this.bundleMap.get(bundleType);
        return new Promise((resolve, reject) => {
            bundle.loadDir(`image/spriteFrame/${path}`, function (err, spriteFrame: SpriteFrame[]) {
                if (err) {
                    console.error('spriteFrame load failed!', path, err);
                    reject && reject();
                    return;
                }
                resolve && resolve(spriteFrame);
            });
        });
    }
    // 提前下载某个 Asset Bundle 到用户空间 pathToBundle 目录下。需要保证用户空间下的 Asset Bundle 和对应原始 Asset Bundle 的结构和内容完全一样
    // 通过 Asset Bundle 在用户空间中的路径进行加载
    // 原生平台
    // assetManager.loadBundle(jsb.fileUtils.getWritablePath() + '/pathToBundle/bundleName', (err, bundle) => {
    //     // ...
    // });

    //批量加载资源
    // 加载 textures 目录下的所有资源
    // bundle.loadDir("textures", function(err, assets) {
    //         // ...
    //     });
    // // 加载 textures 目录下的所有 Texture 资源
    // bundle.loadDir("textures", Texture2D, function(err, assets) {
    //         // ...
    //     });

    // 预加载资源
    // Asset Bundle 中提供了 preload 和 preloadDir 接口用于预加载 Asset Bundle 中的资源。

    // 释放 Asset Bundle 中的资源
    // 使用常规的 assetManager.releaseAsset 方法进行释放。
    //  bundle.load(`image/spriteFrame`, SpriteFrame, function (err, spriteFrame) {
    //      assetManager.releaseAsset(spriteFrame);
    //  });
    //  使用 Asset Bundle 提供的 release 方法，通过传入路径和类型进行释放，只能释放在 Asset Bundle 中的单个资源。参数可以与 Asset Bundle 的 load 方法中使用的参数一致。
    //  bundle.load(`image/spriteFrame`, SpriteFrame, function (err, spriteFrame) {
    //      bundle.release(`image`, SpriteFrame);
    //  });

    public loadDirSprite(path: string, type: typeof SpriteFrame = SpriteFrame, bundleType: BundleType = BundleType.resourcesBundle) {
        let bundle = this.getBundle(bundleType);
        return new Promise<SpriteFrame[]>((resolve, reject) => {
            bundle.loadDir<SpriteFrame>(path, type, (err, res) => {
                if (err) {
                    reject(err)
                    return
                }
                resolve(res)
            })
        })
    }
    /**
    * 获取文本数据
    * @param fileName 文件名
    * @param cb  回调函数
    */
    public getTextData(fileName: string, cb: Function, bundleType: BundleType = BundleType.resourcesBundle) {
        let bundle = this.getBundle(bundleType);
        this.loadRes(bundle, "datas/" + fileName, null, function (err: any, content: TextAsset) {
            if (err) {
                console.error(err.message || err);
                return;
            }

            let text: string = content.text;
            cb(err, text);
        });
    }
    /** 清理掉所有bundle 版本更新时调用 
     * @deprecated 已弃用
    */
    releaseAllBundle() {
        let keys = Object.keys(BundleType);
        for (const key of keys) {
            // 获取 Assert Bundle 
            let bundle = assetManager.getBundle(BundleType[key]);
            if (bundle) {
                // 释放所有资源
                bundle.releaseAll();
                // 移除Assert Bundle
                assetManager.removeBundle(bundle);
            }
        }
    }

}